package com.db.main;

import android.text.TextUtils;
import cn.hutool.core.util.ArrayUtil;
import com.db.adjust.ActivityPackage;
import com.db.adjust.PackageBuilder;
import com.db.adjust.ResponseData;
import com.db.adjust.UtilNetworking;
import com.db.env.Machine;
import com.db.utils.*;
import com.db.utils.okhttp.HttpClientUtil;
import org.apache.http.client.methods.CloseableHttpResponse;
import org.apache.http.util.EntityUtils;
import org.json.JSONObject;

import java.io.IOException;
import java.text.MessageFormat;
import java.util.*;
import java.util.stream.Collectors;

import static com.db.main.Constant.ALIVE_URL;

public class IOSTask extends Task {

    public IOSTask(JSONObject taskJson) {
        super(taskJson);
    }

    @Override
    public void run() {
        String uuid = Util.createUuid();

        // 是否上传信息用于留存业务
        String keeps = taskJson.get("keeps") == JSONObject.NULL ? "" : (String) taskJson.get("keeps");

        // 包Info
        int taskId = taskJson.getInt("id");
        String packageName = taskJson.getString("packageName");
        if (packageName == null || "null".equals(packageName)) {
            LogUtil.logApp("iOS Task Exception", packageName + " is null or empty" + ", packageName=" + packageName);
            return;
        }
        AppInfoUtil.AppInfo appInfoObj = AppInfoUtil.getAppInfo(packageName, "ios");
        if (appInfoObj == null) {
            LogUtil.logApp("AppInfo Exception", packageName + " appInfo not found");
            return;
        }
        String appInfo = appInfoObj.appInfo;
        String algorithm = appInfoObj.algorithm;
        String target = appInfoObj.target;
        String programme = appInfoObj.programme;
        String signatureMeta = appInfoObj.signatureMeta;
        String campaign = appInfoObj.campaign;
        String adgroup = appInfoObj.adgroup;
        String creative = appInfoObj.creative;
        String callback = appInfoObj.callback;
        String way = appInfoObj.way;
        LogUtil.logApp("AppInfo", MessageFormat.format("adjust-sdk: {0}, program:{1}", way, programme));
        LogUtil.logApp("AppInfo", MessageFormat.format("appInfo: {0}", appInfo.trim()));
        JSONObject appJson = new JSONObject(appInfo);

        // 任务国家iso
        String countryStr = taskJson.getString("countries");
        String[] countries = countryStr.split(",");
        if (countries.length == 0) {
            LogUtil.logApp("Task Exception", "No Countries!");
            return;
        }
        String iso = countries[RandomUtil.getRange(0, countries.length)];
        iso = iso.trim();
        LogUtil.logApp("AppInfo", MessageFormat.format("uuid: {0}, countries: {1}, iso: {2}", uuid, countryStr, iso));

        // 代理运营商，h5_site_01_18（kkoip）、h5_site_51（ipidea）、ip_feng6（ppfly）
        String ipProvider = taskJson.get("ipProvider") != null ? taskJson.get("ipProvider").toString() : "ip_feng6";
        // 如果iso属于HK、TW，则使用ip-idea
//        if ("HK".equalsIgnoreCase(iso) || "TW".equalsIgnoreCase(iso)) {
//            ipProvider = "h5_site_51";
//        }

        // 代理测试
        LogUtil.logApp("ProxyProvider", "代理ip供应商：" + ipProvider);

        String ipWeight = taskJson.getString("ipWeight");
        String clickURL = taskJson.getString("offerShortUrl");

        // CTIT区间
        int minCtit = taskJson.get("minCtit") == JSONObject.NULL ? 20 : taskJson.getInt("minCtit");
        int maxCtit = taskJson.get("maxCtit") == JSONObject.NULL ? 50 : taskJson.getInt("maxCtit");
        if (minCtit < 20) {
            minCtit = 20;
        }
        if (maxCtit < 50) {
            maxCtit = 50;
        }
        LogUtil.logApp("TaskInfo", MessageFormat.format("uuid:{0}, taskId:{1}, minCtit:{2}, maxCtit:{3}", uuid, String.valueOf(taskId), minCtit, maxCtit));

        // 随机iOS设备信息
        IOSDevice iosDevice = IOSDevice.getInstance(iso);
        LogUtil.logApp("DeviceInfo", "uuid:" + uuid + ", iOS deviceStr:" + iosDevice);

        // 请求事件：成功拿到任务信息
        report(iosDevice.idfa, taskJson.getInt("id"), 1);
        Main.req.getAndIncrement();

        ProxyUtil proxyUtil = new ProxyUtil(iso, ipProvider, ipWeight, true);

        long clickTimeStampMil;
        int count = 0;
        IpInfo.DataBean ipData = null;
        String redirectUrl = "";
        String idfa = iosDevice.idfa;
        String msgDetail = ", iOS, uuid:" + uuid + ", taskId:" + taskId + ", idfa:" + idfa + ", packageName:" + packageName;

        while (true) {
            LogUtil.logApp("Recreated", "recreated...");

            // 获取代理IP
            ipData = proxyUtil.getIp(uuid);
            if (ipData == null) {
                LogUtil.logApp("ProxyInfo Exception", "uuid:" + uuid + ", ipData is null");
                return;
            }
            LogUtil.logApp("ProxyIpInfo", ipData.toString());
            clickTimeStampMil = System.currentTimeMillis();
            try {
                Map<String, String> header = new HashMap<>();
                LogUtil.logApp("DeviceInfo", "ua:" + iosDevice.ua);
                header.put("User-Agent", iosDevice.ua);
                String url = clickURL.replace("{aff_click_id}", UUID.randomUUID().toString());
                url = url.replace("{idfa}", iosDevice.idfa);
                url = url.replace("{idfv}", iosDevice.idfv);

                String ip = ipData.getIp();
                if (ip != null) {
                    url = url.replace("{ip}", ip);
                }

                if (count++ == 0) {
                    // 点击事件：有代理才去点击
                    report(iosDevice.idfa, taskJson.getInt("id"), 3);
                }

                redirectUrl = click(uuid, taskId, idfa, url, header, ipData, campaign, adgroup, creative, callback);
                LogUtil.logApp("RedirectUrl", "重定向URL:" + redirectUrl);
                if (!redirectUrl.isEmpty()) {
                    LogUtil.logApp("RedirectUrl", "重定向URL非空");
                    break;
                } else {
                    LogUtil.logApp("RedirectUrl", "重定向URL为空");
                }

                if (count > 5) {
                    break;
                }
            } catch (Exception e) {
                LogUtil.logApp("Redirect Exception", "uuid:" + uuid + ", clickURL:" + clickURL + ", Exception Message:" + e.getMessage());
                e.printStackTrace();
                continue;
            }
            break;
        }
        LogUtil.logApp("Redirect跳转结束", "url302:" + redirectUrl);

        String[] tags = {
                "referrer=", "https://apps.apple.com", "https://view.adjust.com",
                "https://app.adjust.com", "https://itunes.apple.com",
                "apkpure.net", "trip.com",
        };
        if (filterUrl(redirectUrl, tags)) {
            // 下载事件：成功跳转到下载页面
            report(iosDevice.idfa, taskJson.getInt("id"), 4);
            LogUtil.logApp("Mock Download", "Redirect Succeed" + msgDetail + ", url302:" + redirectUrl);

            int randomSleepTime = RandomUtil.getRange(minCtit * 1000, (maxCtit - minCtit) * 1000);
            long minEventTime = System.currentTimeMillis() + randomSleepTime;

            LogUtil.logApp("Mock Download", "uuid:" + uuid + ", randomSleepTime:" + randomSleepTime + ", minEventTime:" + minEventTime);

            //launchInstall
            while (true) {
                LogUtil.log("Mock Install", "into launch install");
                if (System.currentTimeMillis() > minEventTime) {
                    LogUtil.logApp("Mock Install", "Installed Succeed" + msgDetail);
                    Machine machine = Machine.parseMachineIOS(iosDevice, appJson, iso);
                    PackageBuilder packageBuilder = machine.createPackageBuilder(iso);
                    packageBuilder.updateSkadnRegisteredAt(System.currentTimeMillis());
                    packageBuilder.updateCreateTime(System.currentTimeMillis());
                    packageBuilder.setTarget(target);

                    packageBuilder.setAlgorithm(algorithm);
                    packageBuilder.setSignatureMeta(signatureMeta);
                    packageBuilder.setProgramme(programme);
                    packageBuilder.setAdjustAlgoType(way);

                    // 1：上报Session给adjust
                    LogUtil.logApp("Mock Install", "Ready to upload session to adjust");
                    ActivityPackage sessionPackage = packageBuilder.buildSessionPackage(uuid, way);
                    Map<String, Object> sessionDetailsMap = post2AdjustByHttpClient(sessionPackage, ipData, iso, iosDevice.ua, uuid);

                    // 2：上报Click给adjust
                    packageBuilder.updateCreateTime(System.currentTimeMillis());
                    LogUtil.logApp("Mock Install", "Ready to upload click to adjust");
                    ActivityPackage clickPackage = packageBuilder.buildClickPackage("apple_ads", uuid, way);
                    post2AdjustByHttpClient(clickPackage, ipData, iso, iosDevice.ua, uuid);

                    try {
                        Thread.sleep(2000);
                    } catch (InterruptedException e) {
                        throw new RuntimeException(e);
                    }

                    // 3：从adjust获取归因结果
                    packageBuilder.updateCreateTime(System.currentTimeMillis());
                    String initiatedBy = "backend";
                    if (sessionDetailsMap != null) {
                        if (sessionDetailsMap.get("result").toString().contains("ask_in")) {
                            initiatedBy = "sdk";
                        }
                    }
                    ActivityPackage attributionPackage = packageBuilder.buildAttributionPackage(initiatedBy, uuid, way);
                    LogUtil.logApp("Mock Install", "Ready to get attribution from adjust");
                    CloseableHttpResponse response = getFromAdjustByHttpClient(attributionPackage, ipData, iso, iosDevice.ua, uuid);
                    estimateAdjustAttribution2(response, sessionDetailsMap, keeps, uuid, idfa, taskId, packageName);

                    break;
                }

                try {
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        } else {
            LogUtil.logApp("Mock Download", "Failure" + msgDetail + ", No features found in the url302:" + redirectUrl);
        }
    }

    /**
     * 上报事件
     * 0 当天限量;
     * 1 请求量;
     * 2 展现量 ;
     * 3 点击量 ;
     * 4 下载量 ;
     * 5 安装量 ;
     * 6 激活量 .
     *
     * @param gaid   gaid
     * @param taskId 任务id
     * @param event  事件类型
     */
    public static void report(String gaid, int taskId, int event) {
        String url = Constant.BASE_URL + "/oneselfPb?gaid=" + gaid + "&taskId=" + taskId + "&event=" + event;

        Downloader.doDownload(url, "GET", "", null);
        LogUtil.logApp("ReportEvent", "EventType:" + event + ", Url:" + url);
    }

    private static String click(String url, Map header, IpInfo.DataBean ipData, StringBuffer referrer) throws IOException {
        return Downloader.doDownloadRedirect(url, "GET", null, header, ipData, referrer);
    }

    private static String click(String url, Map header, IpInfo.DataBean ipData, StringBuffer referrer, List<String> url302List) throws IOException {
        return Downloader.doDownloadRedirect(url, "GET", null, header, ipData, referrer, url302List);
    }

    private static String click(String uuid, int taskId, String url, Map header, IpInfo.DataBean ipData, StringBuffer referrer, List<String> url302List) throws IOException {
        return Downloader.doDownloadRedirect(uuid, taskId, url, "GET", null, header, ipData, referrer, url302List);
    }

    private static String click(String uuid, int taskId, String gaid, String url, Map<String, String> headers, IpInfo.DataBean ipData, String campaign, String adgroup, String creative, String callback) throws IOException {
        return HttpClientUtil.getReferrerNew(uuid, taskId, gaid, url, null, headers, ipData, campaign, adgroup, creative, callback);
    }

    private static ResponseData post2Adjust(ActivityPackage activityPackage, IpInfo.DataBean ipData, String iso, String ua) {
        String url = Constant.ADJUST_URL + activityPackage.getPath();

        try {
            ResponseData responseData = UtilNetworking.createPOSTHttpsURLConnection(url, activityPackage, -1, ipData, iso, ua);
            LogUtil.logApp("post2Adjust-responseData", activityPackage.getPath() + ":" + responseData.toString());
            return responseData;
        } catch (Exception e) {
            LogUtil.logApp("post2Adjust-responseData Exception", e.getMessage());
            e.printStackTrace();
        }
        return null;
    }

    private static ResponseData getFromAdjust(ActivityPackage activityPackage, String basePath, String baseUrl, IpInfo.DataBean ipData, String iso, String ua) {
        try {
            ResponseData responseData = UtilNetworking.createGETHttpsURLConnection(activityPackage, basePath, baseUrl, ipData, iso, ua);
            LogUtil.logApp("getFromAdjust-responseData", activityPackage.getPath() + ":" + responseData.toString());
            return responseData;
        } catch (Exception e) {
            LogUtil.logApp("getFromAdjust-responseData Exception", e.getMessage());
            e.printStackTrace();
        }

        return null;
    }

    /**
     * 识别归因结果
     *
     * @param response 归因请求结果
     * @param idfa     idfa
     * @param taskId   任务id
     * @return 归因结果
     */
    private static boolean estimateAdjustAttribution(ResponseData response, String uuid, String idfa, int taskId, String packageName) {
        String taskDetail = ", uuid:" + uuid + ", taskId:" + taskId + ", packageName:" + packageName + ", idfa:" + idfa;
        String failureKey = "Organic";
        String failureKey2 = "No User Consent";

        if (response == null) {
            LogUtil.logApp("Adjust Attribution Error", "adjust归因response为空" + taskDetail);
            return false;
        }

        try {
            LogUtil.logApp("Adjust Attribution", "response:" + response + taskDetail);
            if (!response.success) {
                LogUtil.logApp("Adjust Attribution Error", "请求adjust归因结果失败" + taskDetail);
                return false;
            }

            JSONObject jsonResponse = response.jsonResponse;
            JSONObject attributionJson = jsonResponse.getJSONObject("attribution");
            if (attributionJson == null) {
                LogUtil.logApp("Adjust Attribution Error", "adjust归因attributionJson为空" + taskDetail);
                return false;
            }

            String tracker_name = attributionJson.getString("tracker_name");
            if (tracker_name != null && !failureKey.equals(tracker_name) && !failureKey2.equals(tracker_name)) {
                LogUtil.logApp("Adjust Attribution", "归因成功" + taskDetail);
                // 安装事件：归因成功才计算一个
                report(idfa, taskId, 5);
                return true;
            } else {
                LogUtil.logApp("Adjust Attribution", "归因失败" + taskDetail);
            }
        } catch (Exception e) {
            LogUtil.logApp("Adjust Attribution Exception", "识别归因结果发生异常：" + response + ", TaskDetail:" + taskDetail);
            e.printStackTrace();
        }

        return false;
    }

    private static Map<String, Object> post2AdjustByHttpClient(ActivityPackage activityPackage, IpInfo.DataBean ipData, String iso, String ua, String uuid) {
        String url = Constant.ADJUST_URL + activityPackage.getPath();

        try {
            LogUtil.logApp("post2AdjustByHttpClient", "uuid:" + uuid + ", url:" + url);
            Map<String, Object> details = UtilNetworking.createPOSTHttpsURLConnection2(uuid, url, activityPackage, ipData, iso, ua);
            LogUtil.logApp("post2AdjustByHttpClient", "uuid:" + uuid + ", " + activityPackage.getPath() + ":" + details.get("result").toString());
            return details;
        } catch (Exception e) {
            LogUtil.logApp("post2AdjustByHttpClient Exception", e.getMessage());
            e.printStackTrace();
        }

        return null;
    }

    private static CloseableHttpResponse getFromAdjustByHttpClient(ActivityPackage activityPackage, IpInfo.DataBean ipData, String iso, String ua, String uuid) {
        String url = Constant.ADJUST_URL + "/" + activityPackage.getPath();
        CloseableHttpResponse response = null;

        try {
            // 获取归因结果
            LogUtil.logApp("getFromAdjustByHttpClient", "uuid:" + uuid + ", url:" + url);
            response = UtilNetworking.createGETHttpsURLConnection2(uuid, activityPackage, url, ipData, iso, ua);
        } catch (Exception e) {
            LogUtil.logApp("getFromAdjustByHttpClient Exception", e.getMessage());
            e.printStackTrace();
        }

        return response;
    }

    private static boolean estimateAdjustAttribution2(CloseableHttpResponse response, Map<String, Object> sessionDetailsMap,
                                                      String keeps,
                                                      String uuid, String gaid,
                                                      int taskId, String packageName) {
        String taskDetail = ", uuid:" + uuid + ", taskId:" + taskId + ", packageName:" + packageName + ", idfa:" + gaid;
        String attributionResult = "";

        String invalidSign = "Invalid Signature";
        String untrustedDevices = "Untrusted Devices";

        try {
            // 获取归因结果
            attributionResult = EntityUtils.toString(response.getEntity(), "UTF-8");
            if (attributionResult.isEmpty()) {
                LogUtil.logApp("Adjust Attribution Error", "adjust归因response为空" + taskDetail);
                return false;
            }

            LogUtil.logApp("Adjust Attribution", "Attribution Response:" + attributionResult + taskDetail);
            JSONObject jsonResponse = new JSONObject(attributionResult);
            if (jsonResponse.has("attribution")) {
                JSONObject attributionJson = jsonResponse.getJSONObject("attribution");
                String trackerName = attributionJson.getString("tracker_name");
                if (trackerName != null && !"Organic".equals(trackerName) && !trackerName.contains(invalidSign) && !trackerName.contains(untrustedDevices)) {
                    LogUtil.logApp("Adjust Attribution", "归因成功" + taskDetail);
                    // 安装事件：归因成功才计算一个
                    report(gaid, taskId, 5);

                    // 上传留存信息
                    if (!TextUtils.isEmpty(keeps)) {
                        LogUtil.logApp("Adjust Attribution", "需要上报留存数据：" + taskDetail);
                        reportSessionInfo2Server(uuid, taskId, sessionDetailsMap);
                    }
                    return true;
                } else {
                    LogUtil.logApp("Adjust Attribution", "归因失败" + taskDetail);
                }
            } else {
                LogUtil.logApp("Adjust Attribution", "归因失败, 响应结果没有特定Key (attribution), " + jsonResponse + taskDetail);
                if (jsonResponse.has("error")) {
                    if (jsonResponse.getString("error").contains("Data residency policy violation")) {
                        LogUtil.logApp("Adjust Attribution", "归因失败, Data residency policy violation" + taskDetail);
                    }
                }
                return false;
            }
        } catch (Exception e) {
            LogUtil.logApp("Adjust Attribution Exception", "识别归因结果发生异常：" + attributionResult + ", TaskDetail:" + taskDetail);
            e.printStackTrace();
        }

        return false;
    }

    private static void reportSessionInfo2Server(String uuid, int taskId, Map<String, Object> sessionParamsMap) {
        Map<String, String> params = new HashMap<>();
        Map<String, String> headers = new HashMap<>();

        try {
            headers = (Map<String, String>) sessionParamsMap.get("headers");
            params = (Map<String, String>) sessionParamsMap.get("params");

            params.put("Client-SDK", headers.get("Client-SDK"));
            params.put("User-Agent", headers.get("User-Agent"));

            String paramsString = params.entrySet()
                    .stream()
                    .map(entry -> entry.getKey() + "=" + entry.getValue())
                    .collect(Collectors.joining("&"));

            JSONObject dataJson = new JSONObject();
            dataJson.put("taskId", String.valueOf(taskId));
            dataJson.put("data", paramsString);

            LogUtil.logApp("reportSession", "uuid:" + uuid + ", 留存参数：" + dataJson.toString());

            String result = HttpClientUtil.doPostRawData(ALIVE_URL, dataJson.toString());
            if ("ok".equals(result)) {
                LogUtil.logApp("reportSession", "uuid:" + uuid + ", 留存数据上报成功");
            } else {
                LogUtil.logApp("reportSession", "uuid:" + uuid + ", 留存数据上报失败，" + result);
            }
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
    }

    private static boolean filterUrl(String url, String[] tags) {
        for (String tag : tags) {
            if (url.contains(tag)) {
                return true;
            }
        }
        return false;
    }

}
